package com.sunday.common.http.apache;

import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreaker;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreakerStrategy;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.EventObserverRegistry;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.util.TimeUtil;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import java.util.ArrayList;
import java.util.List;

@Slf4j
@SpringBootApplication
public class Application {

    static Gson gson = new GsonBuilder().setPrettyPrinting().create();

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
        initFlowQpsRule();
        initDegradeRule();
        registerStateChangeObserver();
    }

    private static void registerStateChangeObserver() {
        EventObserverRegistry.getInstance().addStateChangeObserver("logging",
                (prevState, newState, rule, snapshotValue) -> {
                    if (newState == CircuitBreaker.State.OPEN) {
                        log.info(String.format("[%s]%s -> OPEN at %d, snapshotValue=%.2f", rule.getResource(), prevState.name(),
                                TimeUtil.currentTimeMillis(), snapshotValue));
                    } else {
                        log.info(String.format("[%s]%s -> %s at %d", rule.getResource(), prevState.name(), newState.name(),
                                TimeUtil.currentTimeMillis()));
                    }
                });
    }

    private static void initDegradeRule() {
        List<DegradeRule> rules = new ArrayList<>();
        DegradeRule rule = new DegradeRule("httpclient:/http/api/get/sleep2")
                // 熔断策略，支持慢调用比例/异常比例/异常数策略
                .setGrade(CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType())
                // 慢调用比例模式下为慢调用临界 RT（超出该值计为慢调用）；异常比例/异常数模式下为对应的阈值（单位为 ms）
                // Max allowed response time
                .setCount(1_000)
                // 熔断时长，单位为 s
                // Retry timeout (in second)
                .setTimeWindow(10)
                // 慢调用比例阈值，仅慢调用比例模式有效（1.8.0 引入）
                // Circuit breaker opens when slow request ratio > 30%
                .setSlowRatioThreshold(0.3)
                // 熔断触发的最小请求数，请求数小于该值时即使异常比率超出阈值也不会熔断（1.7.0 引入）
                .setMinRequestAmount(2)
                // 统计时长（单位为 ms），如 60*1000 代表分钟级（1.8.0 引入）
                .setStatIntervalMs(20_000);
        rules.add(rule);
        log.info("{}", gson.toJson(rules));
        DegradeRuleManager.loadRules(rules);
    }

    private static void initFlowQpsRule() {
        List<FlowRule> rules = new ArrayList<FlowRule>();
        FlowRule rule1 = new FlowRule();
        rule1.setResource("httpclient:/http/api/get/easy");
        // set limit qps to 20
        rule1.setCount(1);
        rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rule1.setLimitApp("default");
        rules.add(rule1);
        log.info("{}", gson.toJson(rules));
        FlowRuleManager.loadRules(rules);
    }

}
